package helper

import (
	"crypto/md5"
	"crypto/tls"
	"fmt"
	"go-oj-backend/v1/define"
	"go/parser"
	"go/token"
	"math/rand"
	"net/smtp"
	"os"
	"regexp/syntax"
	"strconv"
	"strings"

	"github.com/dgrijalva/jwt-go"
	uuid "github.com/satori/go.uuid"

	"github.com/jordan-wright/email"
)

type UserClaim struct {
	Identity string `json:"identity"`
	Name     string `json:"name"`
	IsAdmin  int    `json:"is_admin"`
	jwt.StandardClaims
}

func GetMd5(s string) string {
	return fmt.Sprintf("%x", md5.Sum([]byte(s)))
}

func GenerateToken(identity, name string, isAdmin int) (string, error) {
	userclaim := &UserClaim{
		Identity:       identity,
		Name:           name,
		IsAdmin:        isAdmin,
		StandardClaims: jwt.StandardClaims{},
	}

	token := jwt.NewWithClaims(jwt.SigningMethodHS256, userclaim)
	tokenString, err := token.SignedString(define.MyKey)
	if err != nil {
		return "", err
	}
	return tokenString, nil
}

func AnalyseToken(tokenString string) (*UserClaim, error) {
	userClaim := &UserClaim{}
	token, err := jwt.ParseWithClaims(tokenString, userClaim, func(token *jwt.Token) (interface{}, error) {
		return define.MyKey, nil
	})
	if err != nil {
		return nil, err
	}
	if !token.Valid {
		return nil, fmt.Errorf("error: Analyse Token Error: %v", err)
	}
	return userClaim, nil
}

func SendEmailCode(toUserEmail, code string) error {
	e := email.NewEmail()
	e.From = "1690544550@qq.com"
	e.To = []string{toUserEmail}
	e.Subject = "验证码"
	e.Text = []byte("您的验证码为：" + code)
	err := e.SendWithTLS(
		"smtp.qq.com:465",
		smtp.PlainAuth("", "1690544550@qq.com", define.MailPassword, "smtp.qq.com"),
		&tls.Config{
			InsecureSkipVerify: true,
			ServerName:         "smtp.qq.com",
		},
	)
	if err != nil {
		return err
	}
	return nil
}

func GetUUID() string {
	return uuid.NewV4().String()
}

func GetRandomCode() string {
	code := ""
	for i := 0; i < 6; i++ {
		code += strconv.Itoa(rand.Intn(10))
	}
	return code
}

// CheckGoCodeValid 检查go代码的合法性
func CheckGoCodeValid(path string) (bool, error) {
	b, err := os.ReadFile(path)
	if err != nil {
		return false, err
	}

	code := string(b)
	for i := 0; i < len(code)-6; i++ {
		if code[i:i+6] == "import" {
			var flag byte
			for i = i + 7; i < len(code); i++ {
				if code[i] == ' ' {
					continue
				}
				flag = code[i]
				break
			}
			if flag == '(' {
				for i = i + 1; i < len(code); i++ {
					if code[i] == ')' {
						break
					}
					if code[i] == '"' {
						t := ""
						for i = i + 1; i < len(code); i++ {
							if code[i] == '"' {
								break
							}
							t += string(code[i])
						}
						if _, ok := define.ValidGolangPackageMap[t]; !ok {
							return false, nil
						}
					}
				}
			} else if flag == '"' {
				t := ""
				for i = i + 1; i < len(code); i++ {
					if code[i] == '"' {
						break
					}
					t += string(code[i])
				}
				if _, ok := define.ValidGolangPackageMap[t]; !ok {
					return false, nil
				}
			}
		}
	}
	return true, nil
}

/**
主要改进点：
使用标准库解析代码：通过go/parser包解析代码为 AST，直接提取导入声明，避免手动解析字符串的复杂性和易错性。
简化逻辑：无需手动遍历字符查找import关键字，AST 解析器会自动提取所有导入语句。
处理错误更合理：忽略语法错误（如代码不完整），仅检查已解析的导入声明，提高健壮性。
减少嵌套循环：直接遍历 AST 中的导入节点，代码更清晰易读
*/
// CheckGoCodeValid 检查go代码的合法性
func CheckGoCodeValidV2(path string) (bool, error) {
	// 读取文件内容
	src, err := os.ReadFile(path)
	if err != nil {
		return false, err
	}

	// 解析AST（不处理错误，仅提取导入声明）
	f, err := parser.ParseFile(token.NewFileSet(), path, src, parser.ImportsOnly)
	if err != nil {
		// 忽略语法错误，继续检查已解析的导入
		if _, ok := err.(*syntax.Error); !ok {
			return false, err
		}
	}

	// 检查所有导入的包是否在白名单中
	for _, imp := range f.Imports {
		// 去除导入路径的引号
		pkgPath := strings.Trim(imp.Path.Value, `"`)
		if _, ok := define.ValidGolangPackageMap[pkgPath]; !ok {
			return false, nil
		}
	}

	return true, nil
}
